import Cesium, { Cartographic, GeometryInstance, MaterialAppearance, Primitive, Rectangle } from '../Ces/Cesium';
import WeatherVolumeConeGeometry from '../Weather/WeatherVolumeConeGeometry';
import WeDisplayShaders from './WeDisplayShaders';
import WeConeShaders from './WeConeShaders';
import WeUniform from './WeUniform';

interface WeConeOptions {
  rectangle: Rectangle;
  display: any;
  height?: number;
  radius?: number;
}

const { fixMaterialsSourface } = WeDisplayShaders;
const { WeConeVS, WeConeMat } = WeConeShaders;

class WeCone {
  private _pitchs: number[];
  private _weUniform: WeUniform;
  private _showScan: boolean;
  private _primitive: Primitive;

  constructor(options: WeConeOptions) {
    const { rectangle, display } = options;
    const angles: number[] = [0.5, 1.5, 2.4, 3.4, 4.3, 6.0, 9.9, 14.6, 19.5];

    this._pitchs = angles;
    const position = Cesium.Rectangle.center(rectangle);
    position.height = options.height?? 0;
    const floatUniform = {
      conePitchNum: this._pitchs.length,
      coneAlpha: 1.0,
      coneRadius: options.radius?? 460000.0,
      coneScale: 1.0,
      coneScanNodataAlpha: 0.33,
      lastFrameNumber: 0.0,
    };

    if (this._pitchs.length === 9) {
      this._pitchs.push(0);
    }

    this._weUniform = new WeUniform({ floatUniform, name: 'u_WeCone' });

    this._showScan = false;

    this.createPrimitive(display, position);
  }

  get conePitchNum(): number {
    return this._weUniform.Properties.conePitchNum;
  }

  set conePitchNum(value: number) {
    this._weUniform.Properties.conePitchNum = value;
  }

  get scale(): number {
    return this._weUniform.Properties.coneScale;
  }

  set scale(value: number) {
    this._weUniform.Properties.coneScale = value;
  }

  get alpha(): number {
    return this._weUniform.Properties.coneAlpha;
  }

  set alpha(value: number) {
    this._weUniform.Properties.coneAlpha = value;
  }

  get radius(): number {
    return this._weUniform.Properties.coneRadius;
  }

  set radius(value: number) {
    this._weUniform.Properties.coneRadius = value;
  }

  get lastFrameNumber(): number {
    return this._weUniform.Properties.lastFrameNumber;
  }

  set lastFrameNumber(value: number) {
    this._weUniform.Properties.lastFrameNumber = value;
  }

  get showScan(): boolean {
    return this._showScan;
  }

  set showScan(value: boolean) {
    if (this._showScan !== value) {
      this._showScan = value;
      this.lastFrameNumber = 0.0;
    }
  }

  createAppearence(display: any): MaterialAppearance {
    const translucent = this.alpha < 1.0;
    const uniforms = {
      u_pitchs: this._pitchs,
    };

    this._weUniform.setupUniform(uniforms);
    display.setupUniform(uniforms);

    const baseFragmentSource1 = display.getFragmentShaderSource(true, true, true);
    const baseFragmentSource2 = display.getFragmentShaderSource(true, true, false);

    let vertexShaderSource = `
    ${this._weUniform.getSource(true)}
    ${baseFragmentSource1}
    ${WeConeVS}
  `;

    let fragmentShaderSource = `
    ${this._weUniform.getSource(false)}
    ${baseFragmentSource2}
    ${WeConeMat}
    `;

    vertexShaderSource = fixMaterialsSourface(vertexShaderSource, uniforms);
    fragmentShaderSource = fixMaterialsSourface(fragmentShaderSource, uniforms);

    const appearance = new Cesium.MaterialAppearance({
      renderState: {
        depthTest: { enabled: true },
        depthMask: true,
        blending: Cesium.BlendingState.ALPHA_BLEND,
        cull: {
          enabled: false,
          face: Cesium.CullFace.FRONT,
        },
      },
      translucent,
      vertexShaderSource,
      material: new Cesium.Material({
        translucent,
        fabric: {
          uniforms,
          source: fragmentShaderSource,
        },
      }),
    });

    display.fixMaterials(appearance.material);

    return appearance;
  }

  createPrimitive(display: any, position: Cartographic): void {
    const radius = this.radius;

    let i = 0;
    const geometryInstances: GeometryInstance[] = [];

    for (; i < this.conePitchNum; i++) {
      const length = 1;
      const topRadius = radius;

      const geometry = WeatherVolumeConeGeometry.createGeometry(
        new WeatherVolumeConeGeometry({
          length,
          topRadius,
          bottomRadius: 1,
          slices: 128,
          vertexFormat: Cesium.VertexFormat.POSITION_NORMAL_AND_ST,
        }),
      );

      geometryInstances.push(new Cesium.GeometryInstance({
        geometry,
      }));
    }

    const cartesion = Cesium.Cartographic.toCartesian(position, undefined, new Cesium.Cartesian3());
    const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(cartesion);
    const appearance = this.createAppearence(display);

    this._primitive = new Cesium.Primitive({
      geometryInstances,
      asynchronous: false,
      modelMatrix,
      appearance,
    });
  }

  update(frameState): void {
    //@ts-ignore
    this._primitive.update(frameState);

    if (this._showScan) {
      if (this.lastFrameNumber === 0.0) {
        this.lastFrameNumber = frameState.frameNumber;
      }
    }
  }

  destroy(): any {
    //@ts-ignore
    this._primitive = this._primitive && this._primitive.destroy();

    return Cesium.destroyObject(this);
  }

  isdestroyed(): boolean {
    return false;
  }
}

export default WeCone;
